/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"ScriptLoader.h"#include"ScriptLoadHandler.h"#include"ScriptLoadRequest.h"#include"ScriptTrace.h"#include"ModuleLoadRequest.h"#include"ModuleScript.h"#include"prsystem.h"#include"jsapi.h"#include"jsfriendapi.h"#include"js/Utility.h"#include"xpcpublic.h"#include"nsCycleCollectionParticipant.h"#include"nsIContent.h"#include"nsJSUtils.h"#include"mozilla/dom/DocGroup.h"#include"mozilla/dom/Element.h"#include"mozilla/dom/ScriptSettings.h"#include"mozilla/dom/SRILogHelper.h"#include"nsGkAtoms.h"#include"nsNetUtil.h"#include"nsIScriptGlobalObject.h"#include"nsIScriptContext.h"#include"nsIScriptSecurityManager.h"#include"nsIPrincipal.h"#include"nsJSPrincipals.h"#include"nsContentPolicyUtils.h"#include"nsIHttpChannel.h"#include"nsIHttpChannelInternal.h"#include"nsIClassOfService.h"#include"nsICacheInfoChannel.h"#include"nsITimedChannel.h"#include"nsIScriptElement.h"#include"nsIDOMHTMLScriptElement.h"#include"nsIDocShell.h"#include"nsContentUtils.h"#include"nsUnicharUtils.h"#include"nsAutoPtr.h"#include"nsIXPConnect.h"#include"nsError.h"#include"nsThreadUtils.h"#include"nsDocShellCID.h"#include"nsIContentSecurityPolicy.h"#include"mozilla/Logging.h"#include"nsCRT.h"#include"nsContentCreatorFunctions.h"#include"nsProxyRelease.h"#include"nsSandboxFlags.h"#include"nsContentTypeParser.h"#include"nsINetworkPredictor.h"#include"mozilla/ConsoleReportCollector.h"#include"mozilla/AsyncEventDispatcher.h"#include"mozilla/Attributes.h"#include"mozilla/Telemetry.h"#include"mozilla/TimeStamp.h"#include"mozilla/Unused.h"#include"nsIScriptError.h"#include"nsIOutputStream.h"usingJS::SourceBufferHolder;namespacemozilla{namespacedom{LazyLogModuleScriptLoader::gCspPRLog("CSP");LazyLogModuleScriptLoader::gScriptLoaderLog("ScriptLoader");#define LOG(args) \ MOZ_LOG(gScriptLoaderLog, mozilla::LogLevel::Debug, args)// Alternate Data MIME type used by the ScriptLoader to register that we want to// store bytecode without reading it.staticNS_NAMED_LITERAL_CSTRING(kNullMimeType,"javascript/null");//////////////////////////////////////////////////////////////// ScriptLoader::PreloadInfo//////////////////////////////////////////////////////////////inlinevoidImplCycleCollectionUnlink(ScriptLoader::PreloadInfo&aField){ImplCycleCollectionUnlink(aField.mRequest);}inlinevoidImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback&aCallback,ScriptLoader::PreloadInfo&aField,constchar*aName,uint32_taFlags=0){ImplCycleCollectionTraverse(aCallback,aField.mRequest,aName,aFlags);}//////////////////////////////////////////////////////////////// ScriptLoader//////////////////////////////////////////////////////////////NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScriptLoader)NS_INTERFACE_MAP_ENDNS_IMPL_CYCLE_COLLECTION(ScriptLoader,mNonAsyncExternalScriptInsertedRequests,mLoadingAsyncRequests,mLoadedAsyncRequests,mDeferRequests,mXSLTRequests,mParserBlockingRequest,mPreloads,mPendingChildLoaders,mFetchedModules)NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoader)NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoader)ScriptLoader::ScriptLoader(nsIDocument*aDocument):mDocument(aDocument),mParserBlockingBlockerCount(0),mBlockerCount(0),mNumberOfProcessors(0),mEnabled(true),mDeferEnabled(false),mDocumentParsingDone(false),mBlockingDOMContentLoaded(false),mLoadEventFired(false),mReporter(newConsoleReportCollector()){}ScriptLoader::~ScriptLoader(){mObservers.Clear();if(mParserBlockingRequest){mParserBlockingRequest->FireScriptAvailable(NS_ERROR_ABORT);}for(ScriptLoadRequest*req=mXSLTRequests.getFirst();req;req=req->getNext()){req->FireScriptAvailable(NS_ERROR_ABORT);}for(ScriptLoadRequest*req=mDeferRequests.getFirst();req;req=req->getNext()){req->FireScriptAvailable(NS_ERROR_ABORT);}for(ScriptLoadRequest*req=mLoadingAsyncRequests.getFirst();req;req=req->getNext()){req->FireScriptAvailable(NS_ERROR_ABORT);}for(ScriptLoadRequest*req=mLoadedAsyncRequests.getFirst();req;req=req->getNext()){req->FireScriptAvailable(NS_ERROR_ABORT);}for(ScriptLoadRequest*req=mNonAsyncExternalScriptInsertedRequests.getFirst();req;req=req->getNext()){req->FireScriptAvailable(NS_ERROR_ABORT);}// Unblock the kids, in case any of them moved to a different document// subtree in the meantime and therefore aren't actually going away.for(uint32_tj=0;j<mPendingChildLoaders.Length();++j){mPendingChildLoaders[j]->RemoveParserBlockingScriptExecutionBlocker();}}// Collect telemtry data about the cache information, and the kind of source// which are being loaded, and where it is being loaded from.staticvoidCollectScriptTelemetry(nsIIncrementalStreamLoader*aLoader,ScriptLoadRequest*aRequest){usingnamespacemozilla::Telemetry;// Skip this function if we are not running telemetry.if(!CanRecordExtended()){return;}// Report the type of source, as well as the size of the source.if(aRequest->IsLoadingSource()){if(aRequest->mIsInline){AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Inline);nsAutoStringinlineData;aRequest->mElement->GetScriptText(inlineData);Accumulate(DOM_SCRIPT_INLINE_SIZE,inlineData.Length());}else{AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::SourceFallback);Accumulate(DOM_SCRIPT_SOURCE_SIZE,aRequest->mScriptText.length());}}else{MOZ_ASSERT(aRequest->IsLoading());if(aRequest->IsSource()){AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Source);Accumulate(DOM_SCRIPT_SOURCE_SIZE,aRequest->mScriptText.length());}else{MOZ_ASSERT(aRequest->IsBytecode());AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::AltData);Accumulate(DOM_SCRIPT_BYTECODE_SIZE,aRequest->mScriptBytecode.length());}}// Skip if we do not have any cache information for the given script.if(!aLoader){return;}nsCOMPtr<nsIRequest>channel;aLoader->GetRequest(getter_AddRefs(channel));nsCOMPtr<nsICacheInfoChannel>cic(do_QueryInterface(channel));if(!cic){return;}int32_tfetchCount=0;if(NS_SUCCEEDED(cic->GetCacheTokenFetchCount(&fetchCount))){Accumulate(DOM_SCRIPT_FETCH_COUNT,fetchCount);}}// Helper method for checking if the script element is an event-handler// This means that it has both a for-attribute and a event-attribute.// Also, if the for-attribute has a value that matches "\s*window\s*",// and the event-attribute matches "\s*onload([ \(].*)?" then it isn't an// eventhandler. (both matches are case insensitive).// This is how IE seems to filter out a window's onload handler from a// <script for=... event=...> element.staticboolIsScriptEventHandler(nsIContent*aScriptElement){if(!aScriptElement->IsHTMLElement()){returnfalse;}nsAutoStringforAttr,eventAttr;if(!aScriptElement->GetAttr(kNameSpaceID_None,nsGkAtoms::_for,forAttr)||!aScriptElement->GetAttr(kNameSpaceID_None,nsGkAtoms::event,eventAttr)){returnfalse;}constnsAString&for_str=nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(forAttr);if(!for_str.LowerCaseEqualsLiteral("window")){returntrue;}// We found for="window", now check for event="onload".constnsAString&event_str=nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(eventAttr,false);if(!StringBeginsWith(event_str,NS_LITERAL_STRING("onload"),nsCaseInsensitiveStringComparator())){// It ain't "onload.*".returntrue;}nsAutoString::const_iteratorstart,end;event_str.BeginReading(start);event_str.EndReading(end);start.advance(6);// advance past "onload"if(start!=end&&*start!='('&&*start!=' '){// We got onload followed by something other than space or// '('. Not good enough.returntrue;}returnfalse;}nsresultScriptLoader::CheckContentPolicy(nsIDocument*aDocument,nsISupports*aContext,nsIURI*aURI,constnsAString&aType,boolaIsPreLoad){nsContentPolicyTypecontentPolicyType=aIsPreLoad?nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:nsIContentPolicy::TYPE_INTERNAL_SCRIPT;int16_tshouldLoad=nsIContentPolicy::ACCEPT;nsresultrv=NS_CheckContentLoadPolicy(contentPolicyType,aURI,aDocument->NodePrincipal(),aContext,NS_LossyConvertUTF16toASCII(aType),nullptr,//extra&shouldLoad,nsContentUtils::GetContentPolicy(),nsContentUtils::GetSecurityManager());if(NS_FAILED(rv)||NS_CP_REJECTED(shouldLoad)){if(NS_FAILED(rv)||shouldLoad!=nsIContentPolicy::REJECT_TYPE){returnNS_ERROR_CONTENT_BLOCKED;}returnNS_ERROR_CONTENT_BLOCKED_SHOW_ALT;}returnNS_OK;}boolScriptLoader::ModuleScriptsEnabled(){staticboolsEnabledForContent=false;staticboolsCachedPref=false;if(!sCachedPref){sCachedPref=true;Preferences::AddBoolVarCache(&sEnabledForContent,"dom.moduleScripts.enabled",false);}returnnsContentUtils::IsChromeDoc(mDocument)||sEnabledForContent;}boolScriptLoader::ModuleMapContainsModule(ModuleLoadRequest*aRequest)const{// Returns whether we have fetched, or are currently fetching, a module script// for the request's URL.returnmFetchingModules.Contains(aRequest->mURI)||mFetchedModules.Contains(aRequest->mURI);}boolScriptLoader::IsFetchingModule(ModuleLoadRequest*aRequest)const{boolfetching=mFetchingModules.Contains(aRequest->mURI);MOZ_ASSERT_IF(fetching,!mFetchedModules.Contains(aRequest->mURI));returnfetching;}voidScriptLoader::SetModuleFetchStarted(ModuleLoadRequest*aRequest){// Update the module map to indicate that a module is currently being fetched.MOZ_ASSERT(aRequest->IsLoading());MOZ_ASSERT(!ModuleMapContainsModule(aRequest));mFetchingModules.Put(aRequest->mURI,nullptr);}voidScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest*aRequest,nsresultaResult){// Update module map with the result of fetching a single module script. The// module script pointer is nullptr on error.MOZ_ASSERT(!aRequest->IsReadyToRun());RefPtr<GenericPromise::Private>promise;MOZ_ALWAYS_TRUE(mFetchingModules.Remove(aRequest->mURI,getter_AddRefs(promise)));RefPtr<ModuleScript>ms(aRequest->mModuleScript);MOZ_ASSERT(NS_SUCCEEDED(aResult)==(ms!=nullptr));mFetchedModules.Put(aRequest->mURI,ms);if(promise){if(ms){promise->Resolve(true,__func__);}else{promise->Reject(aResult,__func__);}}}RefPtr<GenericPromise>ScriptLoader::WaitForModuleFetch(ModuleLoadRequest*aRequest){MOZ_ASSERT(ModuleMapContainsModule(aRequest));if(autoentry=mFetchingModules.Lookup(aRequest->mURI)){if(!entry.Data()){entry.Data()=newGenericPromise::Private(__func__);}returnentry.Data();}RefPtr<ModuleScript>ms;MOZ_ALWAYS_TRUE(mFetchedModules.Get(aRequest->mURI,getter_AddRefs(ms)));if(!ms||ms->InstantiationFailed()){returnGenericPromise::CreateAndReject(NS_ERROR_FAILURE,__func__);}returnGenericPromise::CreateAndResolve(true,__func__);}ModuleScript*ScriptLoader::GetFetchedModule(nsIURI*aURL)const{boolfound;ModuleScript*ms=mFetchedModules.GetWeak(aURL,&found);MOZ_ASSERT(found);returnms;}nsresultScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest*aRequest){MOZ_ASSERT(!aRequest->mModuleScript);nsresultrv=CreateModuleScript(aRequest);SetModuleFetchFinishedAndResumeWaitingRequests(aRequest,rv);aRequest->mScriptText.clearAndFree();if(NS_SUCCEEDED(rv)){StartFetchingModuleDependencies(aRequest);}returnrv;}nsresultScriptLoader::CreateModuleScript(ModuleLoadRequest*aRequest){MOZ_ASSERT(!aRequest->mModuleScript);MOZ_ASSERT(aRequest->mBaseURL);nsCOMPtr<nsIScriptGlobalObject>globalObject=GetScriptGlobalObject();if(!globalObject){returnNS_ERROR_FAILURE;}nsCOMPtr<nsIScriptContext>context=globalObject->GetScriptContext();if(!context){returnNS_ERROR_FAILURE;}nsAutoMicroTaskmt;AutoEntryScriptaes(globalObject,"CompileModule",true);boololdProcessingScriptTag=context->GetProcessingScriptTag();context->SetProcessingScriptTag(true);nsresultrv;{// Update our current script.AutoCurrentScriptUpdaterscriptUpdater(this,aRequest->mElement);JSContext*cx=aes.cx();JS::Rooted<JSObject*>module(cx);if(aRequest->mWasCompiledOMT){module=JS::FinishOffThreadModule(cx,aRequest->mOffThreadToken);aRequest->mOffThreadToken=nullptr;rv=module?NS_OK:NS_ERROR_FAILURE;}else{JS::Rooted<JSObject*>global(cx,globalObject->GetGlobalJSObject());JS::CompileOptionsoptions(cx);rv=FillCompileOptionsForRequest(aes,aRequest,global,&options);if(NS_SUCCEEDED(rv)){nsAutoStringinlineData;SourceBufferHoldersrcBuf=GetScriptSource(aRequest,inlineData);rv=nsJSUtils::CompileModule(cx,srcBuf,global,options,&module);}}MOZ_ASSERT(NS_SUCCEEDED(rv)==(module!=nullptr));if(module){aRequest->mModuleScript=newModuleScript(this,aRequest->mBaseURL,module);}}context->SetProcessingScriptTag(oldProcessingScriptTag);returnrv;}staticboolThrowTypeError(JSContext*aCx,ModuleScript*aScript,constnsString&aMessage){JS::Rooted<JSObject*>module(aCx,aScript->ModuleRecord());JS::Rooted<JSScript*>script(aCx,JS::GetModuleScript(aCx,module));JS::Rooted<JSString*>filename(aCx);filename=JS_NewStringCopyZ(aCx,JS_GetScriptFilename(script));if(!filename){returnfalse;}JS::Rooted<JSString*>message(aCx,JS_NewUCStringCopyZ(aCx,aMessage.get()));if(!message){returnfalse;}JS::Rooted<JS::Value>error(aCx);if(!JS::CreateError(aCx,JSEXN_TYPEERR,nullptr,filename,0,0,nullptr,message,&error)){returnfalse;}JS_SetPendingException(aCx,error);returnfalse;}staticboolHandleResolveFailure(JSContext*aCx,ModuleScript*aScript,constnsAString&aSpecifier){// TODO: How can we get the line number of the failed import?nsAutoStringmessage(NS_LITERAL_STRING("Error resolving module specifier: "));message.Append(aSpecifier);returnThrowTypeError(aCx,aScript,message);}staticboolHandleModuleNotFound(JSContext*aCx,ModuleScript*aScript,constnsAString&aSpecifier){// TODO: How can we get the line number of the failed import?nsAutoStringmessage(NS_LITERAL_STRING("Resolved module not found in map: "));message.Append(aSpecifier);returnThrowTypeError(aCx,aScript,message);}staticalready_AddRefed<nsIURI>ResolveModuleSpecifier(ModuleScript*aScript,constnsAString&aSpecifier){// The following module specifiers are allowed by the spec:// - a valid absolute URL// - a valid relative URL that starts with "/", "./" or "../"//// Bareword module specifiers are currently disallowed as these may be given// special meanings in the future.nsCOMPtr<nsIURI>uri;nsresultrv=NS_NewURI(getter_AddRefs(uri),aSpecifier);if(NS_SUCCEEDED(rv)){returnuri.forget();}if(rv!=NS_ERROR_MALFORMED_URI){returnnullptr;}if(!StringBeginsWith(aSpecifier,NS_LITERAL_STRING("/"))&&!StringBeginsWith(aSpecifier,NS_LITERAL_STRING("./"))&&!StringBeginsWith(aSpecifier,NS_LITERAL_STRING("../"))){returnnullptr;}rv=NS_NewURI(getter_AddRefs(uri),aSpecifier,nullptr,aScript->BaseURL());if(NS_SUCCEEDED(rv)){returnuri.forget();}returnnullptr;}staticnsresultRequestedModuleIsInAncestorList(ModuleLoadRequest*aRequest,nsIURI*aURL,bool*aResult){constsize_tImportDepthLimit=100;*aResult=false;size_tdepth=0;while(aRequest){if(depth++==ImportDepthLimit){returnNS_ERROR_FAILURE;}boolequal;nsresultrv=aURL->Equals(aRequest->mURI,&equal);NS_ENSURE_SUCCESS(rv,rv);if(equal){*aResult=true;returnNS_OK;}aRequest=aRequest->mParent;}returnNS_OK;}staticnsresultResolveRequestedModules(ModuleLoadRequest*aRequest,nsCOMArray<nsIURI>&aUrls){ModuleScript*ms=aRequest->mModuleScript;AutoJSAPIjsapi;if(!jsapi.Init(ms->ModuleRecord())){returnNS_ERROR_FAILURE;}JSContext*cx=jsapi.cx();JS::Rooted<JSObject*>moduleRecord(cx,ms->ModuleRecord());JS::Rooted<JSObject*>specifiers(cx,JS::GetRequestedModules(cx,moduleRecord));uint32_tlength;if(!JS_GetArrayLength(cx,specifiers,&length)){returnNS_ERROR_FAILURE;}JS::Rooted<JS::Value>val(cx);for(uint32_ti=0;i<length;i++){if(!JS_GetElement(cx,specifiers,i,&val)){returnNS_ERROR_FAILURE;}nsAutoJSStringspecifier;if(!specifier.init(cx,val)){returnNS_ERROR_FAILURE;}// Let url be the result of resolving a module specifier given module script and requested.ModuleScript*ms=aRequest->mModuleScript;nsCOMPtr<nsIURI>uri=ResolveModuleSpecifier(ms,specifier);if(!uri){HandleResolveFailure(cx,ms,specifier);returnNS_ERROR_FAILURE;}boolisAncestor;nsresultrv=RequestedModuleIsInAncestorList(aRequest,uri,&isAncestor);NS_ENSURE_SUCCESS(rv,rv);if(!isAncestor){aUrls.AppendElement(uri.forget());}}returnNS_OK;}voidScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest*aRequest){MOZ_ASSERT(aRequest->mModuleScript);MOZ_ASSERT(!aRequest->mModuleScript->InstantiationFailed());MOZ_ASSERT(!aRequest->IsReadyToRun());aRequest->mProgress=ModuleLoadRequest::Progress::FetchingImports;nsCOMArray<nsIURI>urls;nsresultrv=ResolveRequestedModules(aRequest,urls);if(NS_FAILED(rv)){aRequest->LoadFailed();return;}if(urls.Length()==0){// There are no descendents to load so this request is ready.aRequest->DependenciesLoaded();return;}// For each url in urls, fetch a module script tree given url, module script's// CORS setting, and module script's settings object.nsTArray<RefPtr<GenericPromise>>importsReady;for(size_ti=0;i<urls.Length();i++){RefPtr<GenericPromise>childReady=StartFetchingModuleAndDependencies(aRequest,urls[i]);importsReady.AppendElement(childReady);}// Wait for all imports to become ready.RefPtr<GenericPromise::AllPromiseType>allReady=GenericPromise::All(GetMainThreadSerialEventTarget(),importsReady);allReady->Then(GetMainThreadSerialEventTarget(),__func__,aRequest,&ModuleLoadRequest::DependenciesLoaded,&ModuleLoadRequest::LoadFailed);}RefPtr<GenericPromise>ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest*aRequest,nsIURI*aURI){MOZ_ASSERT(aURI);RefPtr<ModuleLoadRequest>childRequest=newModuleLoadRequest(aRequest->mElement,aRequest->mJSVersion,aRequest->mCORSMode,aRequest->mIntegrity,this);childRequest->mIsTopLevel=false;childRequest->mURI=aURI;childRequest->mIsInline=false;childRequest->mReferrerPolicy=aRequest->mReferrerPolicy;childRequest->mParent=aRequest;RefPtr<GenericPromise>ready=childRequest->mReady.Ensure(__func__);nsresultrv=StartLoad(childRequest);if(NS_FAILED(rv)){childRequest->mReady.Reject(rv,__func__);returnready;}aRequest->mImports.AppendElement(childRequest);returnready;}boolHostResolveImportedModule(JSContext*aCx,unsignedargc,JS::Value*vp){MOZ_ASSERT(argc==2);JS::CallArgsargs=JS::CallArgsFromVp(argc,vp);JS::Rooted<JSObject*>module(aCx,&args[0].toObject());JS::Rooted<JSString*>specifier(aCx,args[1].toString());// Let referencing module script be referencingModule.[[HostDefined]].JS::Valuevalue=JS::GetModuleHostDefinedField(module);autoscript=static_cast<ModuleScript*>(value.toPrivate());MOZ_ASSERT(script->ModuleRecord()==module);// Let url be the result of resolving a module specifier given referencing// module script and specifier. If the result is failure, throw a TypeError// exception and abort these steps.nsAutoJSStringstring;if(!string.init(aCx,specifier)){returnfalse;}nsCOMPtr<nsIURI>uri=ResolveModuleSpecifier(script,string);if(!uri){returnHandleResolveFailure(aCx,script,string);}// Let resolved module script be the value of the entry in module map whose// key is url. If no such entry exists, throw a TypeError exception and abort// these steps.ModuleScript*ms=script->Loader()->GetFetchedModule(uri);if(!ms){returnHandleModuleNotFound(aCx,script,string);}if(ms->InstantiationFailed()){JS::Rooted<JS::Value>exception(aCx,ms->Exception());JS_SetPendingException(aCx,exception);returnfalse;}*vp=JS::ObjectValue(*ms->ModuleRecord());returntrue;}staticnsresultEnsureModuleResolveHook(JSContext*aCx){if(JS::GetModuleResolveHook(aCx)){returnNS_OK;}JS::Rooted<JSFunction*>func(aCx);func=JS_NewFunction(aCx,HostResolveImportedModule,2,0,"HostResolveImportedModule");if(!func){returnNS_ERROR_FAILURE;}JS::SetModuleResolveHook(aCx,func);returnNS_OK;}voidScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest*aRequest){if(aRequest->IsTopLevel()){MaybeMoveToLoadedList(aRequest);ProcessPendingRequests();}if(aRequest->mWasCompiledOMT){mDocument->UnblockOnload(false);}}boolScriptLoader::InstantiateModuleTree(ModuleLoadRequest*aRequest){// Perform eager instantiation of the loaded module tree.MOZ_ASSERT(aRequest);ModuleScript*ms=aRequest->mModuleScript;MOZ_ASSERT(ms);if(!ms->ModuleRecord()){returnfalse;}AutoJSAPIjsapi;if(NS_WARN_IF(!jsapi.Init(ms->ModuleRecord()))){returnfalse;}nsresultrv=EnsureModuleResolveHook(jsapi.cx());NS_ENSURE_SUCCESS(rv,false);JS::Rooted<JSObject*>module(jsapi.cx(),ms->ModuleRecord());boolok=NS_SUCCEEDED(nsJSUtils::ModuleDeclarationInstantiation(jsapi.cx(),module));JS::RootedValueexception(jsapi.cx());if(!ok){MOZ_ASSERT(jsapi.HasException());if(!jsapi.StealException(&exception)){returnfalse;}MOZ_ASSERT(!exception.isUndefined());}// Mark this module and any uninstantiated dependencies found via depth-first// search as instantiated and record any error.mozilla::Vector<ModuleLoadRequest*,1>requests;if(!requests.append(aRequest)){returnfalse;}while(!requests.empty()){ModuleLoadRequest*request=requests.popCopy();ModuleScript*ms=request->mModuleScript;if(!ms->IsUninstantiated()){continue;}ms->SetInstantiationResult(exception);for(autoimport:request->mImports){if(import->mModuleScript->IsUninstantiated()&&!requests.append(import)){returnfalse;}}}returntrue;}nsresultScriptLoader::RestartLoad(ScriptLoadRequest*aRequest){MOZ_ASSERT(aRequest->IsBytecode());aRequest->mScriptBytecode.clearAndFree();TRACE_FOR_TEST(aRequest->mElement,"scriptloader_fallback");// Start a new channel from which we explicitly request to stream the source// instead of the bytecode.aRequest->mProgress=ScriptLoadRequest::Progress::Loading_Source;nsresultrv=StartLoad(aRequest);if(NS_FAILED(rv)){returnrv;}// Close the current channel and this ScriptLoadHandler as we created a new// one for the same request.returnNS_BINDING_RETARGETED;}nsresultScriptLoader::StartLoad(ScriptLoadRequest*aRequest){MOZ_ASSERT(aRequest->IsLoading());NS_ENSURE_TRUE(mDocument,NS_ERROR_NULL_POINTER);aRequest->mDataType=ScriptLoadRequest::DataType::Unknown;// If this document is sandboxed without 'allow-scripts', abort.if(mDocument->HasScriptsBlockedBySandbox()){returnNS_OK;}if(aRequest->IsModuleRequest()){// Check whether the module has been fetched or is currently being fetched,// and if so wait for it.ModuleLoadRequest*request=aRequest->AsModuleRequest();if(ModuleMapContainsModule(request)){WaitForModuleFetch(request)->Then(GetMainThreadSerialEventTarget(),__func__,request,&ModuleLoadRequest::ModuleLoaded,&ModuleLoadRequest::LoadFailed);returnNS_OK;}// Otherwise put the URL in the module map and mark it as fetching.SetModuleFetchStarted(request);}nsContentPolicyTypecontentPolicyType=aRequest->IsPreload()?nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:nsIContentPolicy::TYPE_INTERNAL_SCRIPT;nsCOMPtr<nsINode>context;if(aRequest->mElement){context=do_QueryInterface(aRequest->mElement);}else{context=mDocument;}nsCOMPtr<nsILoadGroup>loadGroup=mDocument->GetDocumentLoadGroup();nsCOMPtr<nsPIDOMWindowOuter>window=mDocument->GetWindow();NS_ENSURE_TRUE(window,NS_ERROR_NULL_POINTER);nsIDocShell*docshell=window->GetDocShell();nsCOMPtr<nsIInterfaceRequestor>prompter(do_QueryInterface(docshell));nsSecurityFlagssecurityFlags;if(aRequest->IsModuleRequest()){// According to the spec, module scripts have different behaviour to classic// scripts and always use CORS.securityFlags=nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;if(aRequest->mCORSMode==CORS_NONE){securityFlags|=nsILoadInfo::SEC_COOKIES_OMIT;}elseif(aRequest->mCORSMode==CORS_ANONYMOUS){securityFlags|=nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;}else{MOZ_ASSERT(aRequest->mCORSMode==CORS_USE_CREDENTIALS);securityFlags|=nsILoadInfo::SEC_COOKIES_INCLUDE;}}else{securityFlags=aRequest->mCORSMode==CORS_NONE?nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL:nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;if(aRequest->mCORSMode==CORS_ANONYMOUS){securityFlags|=nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;}elseif(aRequest->mCORSMode==CORS_USE_CREDENTIALS){securityFlags|=nsILoadInfo::SEC_COOKIES_INCLUDE;}}securityFlags|=nsILoadInfo::SEC_ALLOW_CHROME;nsCOMPtr<nsIChannel>channel;nsresultrv=NS_NewChannel(getter_AddRefs(channel),aRequest->mURI,context,securityFlags,contentPolicyType,loadGroup,prompter,nsIRequest::LOAD_NORMAL|nsIChannel::LOAD_CLASSIFY_URI);NS_ENSURE_SUCCESS(rv,rv);// To avoid decoding issues, the JSVersion is explicitly guarded here, and the// build-id is part of the JSBytecodeMimeType constant.aRequest->mCacheInfo=nullptr;nsCOMPtr<nsICacheInfoChannel>cic(do_QueryInterface(channel));if(cic&&nsContentUtils::IsBytecodeCacheEnabled()&&aRequest->mJSVersion==JSVERSION_DEFAULT){if(!aRequest->IsLoadingSource()){// Inform the HTTP cache that we prefer to have information coming from the// bytecode cache instead of the sources, if such entry is already registered.LOG(("ScriptLoadRequest (%p): Maybe request bytecode",aRequest));cic->PreferAlternativeDataType(nsContentUtils::JSBytecodeMimeType());}else{// If we are explicitly loading from the sources, such as after a// restarted request, we might still want to save the bytecode after.//// The following tell the cache to look for an alternative data type which// does not exist, such that we can later save the bytecode with a// different alternative data type.LOG(("ScriptLoadRequest (%p): Request saving bytecode later",aRequest));cic->PreferAlternativeDataType(kNullMimeType);}}nsIScriptElement*script=aRequest->mElement;nsCOMPtr<nsIClassOfService>cos(do_QueryInterface(channel));if(cos){if(aRequest->mScriptFromHead&&!(script&&(script->GetScriptAsync()||script->GetScriptDeferred()))){// synchronous head scripts block loading of most other non js/css// content such as imagescos->AddClassFlags(nsIClassOfService::Leader);}elseif(!(script&&script->GetScriptDeferred())){// other scripts are neither blocked nor prioritized unless marked deferredcos->AddClassFlags(nsIClassOfService::Unblocked);}}nsCOMPtr<nsIHttpChannel>httpChannel(do_QueryInterface(channel));if(httpChannel){// HTTP content negotation has little value in this context.rv=httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),NS_LITERAL_CSTRING("*/*"),false);MOZ_ASSERT(NS_SUCCEEDED(rv));rv=httpChannel->SetReferrerWithPolicy(mDocument->GetDocumentURI(),aRequest->mReferrerPolicy);MOZ_ASSERT(NS_SUCCEEDED(rv));nsCOMPtr<nsIHttpChannelInternal>internalChannel(do_QueryInterface(httpChannel));if(internalChannel){rv=internalChannel->SetIntegrityMetadata(aRequest->mIntegrity.GetIntegrityString());MOZ_ASSERT(NS_SUCCEEDED(rv));}}mozilla::net::PredictorLearn(aRequest->mURI,mDocument->GetDocumentURI(),nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,mDocument->NodePrincipal()->OriginAttributesRef());// Set the initiator typensCOMPtr<nsITimedChannel>timedChannel(do_QueryInterface(httpChannel));if(timedChannel){timedChannel->SetInitiatorType(NS_LITERAL_STRING("script"));}nsAutoPtr<mozilla::dom::SRICheckDataVerifier>sriDataVerifier;if(!aRequest->mIntegrity.IsEmpty()){nsAutoCStringsourceUri;if(mDocument->GetDocumentURI()){mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);}sriDataVerifier=newSRICheckDataVerifier(aRequest->mIntegrity,sourceUri,mReporter);}RefPtr<ScriptLoadHandler>handler=newScriptLoadHandler(this,aRequest,sriDataVerifier.forget());nsCOMPtr<nsIIncrementalStreamLoader>loader;rv=NS_NewIncrementalStreamLoader(getter_AddRefs(loader),handler);NS_ENSURE_SUCCESS(rv,rv);returnchannel->AsyncOpen2(loader);}boolScriptLoader::PreloadURIComparator::Equals(constPreloadInfo&aPi,nsIURI*const&aURI)const{boolsame;returnNS_SUCCEEDED(aPi.mRequest->mURI->Equals(aURI,&same))&&same;}classScriptRequestProcessor:publicRunnable{private:RefPtr<ScriptLoader>mLoader;RefPtr<ScriptLoadRequest>mRequest;public:ScriptRequestProcessor(ScriptLoader*aLoader,ScriptLoadRequest*aRequest):Runnable("dom::ScriptRequestProcessor"),mLoader(aLoader),mRequest(aRequest){}NS_IMETHODRun()override{returnmLoader->ProcessRequest(mRequest);}};staticinlineboolParseTypeAttribute(constnsAString&aType,JSVersion*aVersion){MOZ_ASSERT(!aType.IsEmpty());MOZ_ASSERT(aVersion);MOZ_ASSERT(*aVersion==JSVERSION_DEFAULT);nsContentTypeParserparser(aType);nsAutoStringmimeType;nsresultrv=parser.GetType(mimeType);NS_ENSURE_SUCCESS(rv,false);if(!nsContentUtils::IsJavascriptMIMEType(mimeType)){returnfalse;}// Get the version string, and ensure the language supports it.nsAutoStringversionName;rv=parser.GetParameter("version",versionName);if(NS_SUCCEEDED(rv)){*aVersion=nsContentUtils::ParseJavascriptVersion(versionName);}elseif(rv!=NS_ERROR_INVALID_ARG){returnfalse;}returntrue;}staticboolCSPAllowsInlineScript(nsIScriptElement*aElement,nsIDocument*aDocument){nsCOMPtr<nsIContentSecurityPolicy>csp;// Note: For imports NodePrincipal and the principal of the master are// the same.nsresultrv=aDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));NS_ENSURE_SUCCESS(rv,false);if(!csp){// no CSP --> allowreturntrue;}// query the noncensCOMPtr<nsIContent>scriptContent=do_QueryInterface(aElement);nsAutoStringnonce;scriptContent->GetAttr(kNameSpaceID_None,nsGkAtoms::nonce,nonce);boolparserCreated=aElement->GetParserCreated()!=mozilla::dom::NOT_FROM_PARSER;// query the scripttextnsAutoStringscriptText;aElement->GetScriptText(scriptText);boolallowInlineScript=false;rv=csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT,nonce,parserCreated,scriptText,aElement->GetScriptLineNumber(),&allowInlineScript);returnallowInlineScript;}ScriptLoadRequest*ScriptLoader::CreateLoadRequest(ScriptKindaKind,nsIScriptElement*aElement,uint32_taVersion,CORSModeaCORSMode,constSRIMetadata&aIntegrity){if(aKind==ScriptKind::Classic){returnnewScriptLoadRequest(aKind,aElement,aVersion,aCORSMode,aIntegrity);}MOZ_ASSERT(aKind==ScriptKind::Module);returnnewModuleLoadRequest(aElement,aVersion,aCORSMode,aIntegrity,this);}boolScriptLoader::ProcessScriptElement(nsIScriptElement*aElement){// We need a document to evaluate scripts.NS_ENSURE_TRUE(mDocument,false);// Check to see if scripts has been turned off.if(!mEnabled||!mDocument->IsScriptEnabled()){returnfalse;}NS_ASSERTION(!aElement->IsMalformed(),"Executing malformed script");nsCOMPtr<nsIContent>scriptContent=do_QueryInterface(aElement);// Step 13. Check that the script is not an eventhandlerif(IsScriptEventHandler(scriptContent)){returnfalse;}JSVersionversion=JSVERSION_DEFAULT;// Check the type attribute to determine language and version.// If type exists, it trumps the deprecated 'language='nsAutoStringtype;boolhasType=aElement->GetScriptType(type);ScriptKindscriptKind=ScriptKind::Classic;if(!type.IsEmpty()){if(ModuleScriptsEnabled()&&type.LowerCaseEqualsASCII("module")){scriptKind=ScriptKind::Module;}else{NS_ENSURE_TRUE(ParseTypeAttribute(type,&version),false);}}elseif(!hasType){// no 'type=' element// "language" is a deprecated attribute of HTML, so we check it only for// HTML script elements.if(scriptContent->IsHTMLElement()){nsAutoStringlanguage;scriptContent->GetAttr(kNameSpaceID_None,nsGkAtoms::language,language);if(!language.IsEmpty()){if(!nsContentUtils::IsJavaScriptLanguage(language)){returnfalse;}}}}// "In modern user agents that support module scripts, the script element with// the nomodule attribute will be ignored".// "The nomodule attribute must not be specified on module scripts (and will// be ignored if it is)."if(ModuleScriptsEnabled()&&scriptKind==ScriptKind::Classic&&scriptContent->IsHTMLElement()&&scriptContent->HasAttr(kNameSpaceID_None,nsGkAtoms::nomodule)){returnfalse;}// Step 15. and later in the HTML5 specnsresultrv=NS_OK;RefPtr<ScriptLoadRequest>request;if(aElement->GetScriptExternal()){// external scriptnsCOMPtr<nsIURI>scriptURI=aElement->GetScriptURI();if(!scriptURI){// Asynchronously report the failure to create a URI objectNS_DispatchToCurrentThread(NewRunnableMethod("nsIScriptElement::FireErrorEvent",aElement,&nsIScriptElement::FireErrorEvent));returnfalse;}// Double-check that the preload matches what we're asked to load now.mozilla::net::ReferrerPolicyourRefPolicy=mDocument->GetReferrerPolicy();CORSModeourCORSMode=aElement->GetCORSMode();nsTArray<PreloadInfo>::index_typei=mPreloads.IndexOf(scriptURI.get(),0,PreloadURIComparator());if(i!=nsTArray<PreloadInfo>::NoIndex){// preloaded// note that a script-inserted script can steal a preload!request=mPreloads[i].mRequest;request->mElement=aElement;nsStringpreloadCharset(mPreloads[i].mCharset);mPreloads.RemoveElementAt(i);// Double-check that the charset the preload used is the same as// the charset we have now.nsAutoStringelementCharset;aElement->GetScriptCharset(elementCharset);if(elementCharset.Equals(preloadCharset)&&ourCORSMode==request->mCORSMode&&ourRefPolicy==request->mReferrerPolicy&&scriptKind==request->mKind){rv=CheckContentPolicy(mDocument,aElement,request->mURI,type,false);if(NS_FAILED(rv)){// probably plans have changed; even though the preload was allowed seems// like the actual load is not; let's cancel the preload request.request->Cancel();returnfalse;}}else{// Drop the preloadrequest=nullptr;}}if(!request){// no usable preloadSRIMetadatasriMetadata;{nsAutoStringintegrity;scriptContent->GetAttr(kNameSpaceID_None,nsGkAtoms::integrity,integrity);if(!integrity.IsEmpty()){MOZ_LOG(SRILogHelper::GetSriLog(),mozilla::LogLevel::Debug,("ScriptLoader::ProcessScriptElement, integrity=%s",NS_ConvertUTF16toUTF8(integrity).get()));nsAutoCStringsourceUri;if(mDocument->GetDocumentURI()){mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);}SRICheck::IntegrityMetadata(integrity,sourceUri,mReporter,&sriMetadata);}}request=CreateLoadRequest(scriptKind,aElement,version,ourCORSMode,sriMetadata);request->mURI=scriptURI;request->mIsInline=false;request->mReferrerPolicy=ourRefPolicy;// keep request->mScriptFromHead to false so we don't treat non preloaded// scripts as blockers for full page load. See bug 792438.rv=StartLoad(request);if(NS_FAILED(rv)){constchar*message="ScriptSourceLoadFailed";if(rv==NS_ERROR_MALFORMED_URI){message="ScriptSourceMalformed";}elseif(rv==NS_ERROR_DOM_BAD_URI){message="ScriptSourceNotAllowed";}NS_ConvertUTF8toUTF16url(scriptURI->GetSpecOrDefault());constchar16_t*params[]={url.get()};nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,NS_LITERAL_CSTRING("Script Loader"),mDocument,nsContentUtils::eDOM_PROPERTIES,message,params,ArrayLength(params),nullptr,EmptyString(),aElement->GetScriptLineNumber());// Asynchronously report the load failureNS_DispatchToCurrentThread(NewRunnableMethod("nsIScriptElement::FireErrorEvent",aElement,&nsIScriptElement::FireErrorEvent));returnfalse;}}// Should still be in loading stage of script.NS_ASSERTION(!request->InCompilingStage(),"Request should not yet be in compiling stage.");request->mJSVersion=version;if(aElement->GetScriptAsync()){request->mIsAsync=true;if(request->IsReadyToRun()){mLoadedAsyncRequests.AppendElement(request);// The script is available already. Run it ASAP when the event// loop gets a chance to spin.// KVKV TODO: Instead of processing immediately, try off-thread-parsing// it and only schedule a pending ProcessRequest if that fails.ProcessPendingRequestsAsync();}else{mLoadingAsyncRequests.AppendElement(request);}returnfalse;}if(!aElement->GetParserCreated()){// Violate the HTML5 spec in order to make LABjs and the "order" plug-in// for RequireJS work with their Gecko-sniffed code path. See// http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.htmlrequest->mIsNonAsyncScriptInserted=true;mNonAsyncExternalScriptInsertedRequests.AppendElement(request);if(request->IsReadyToRun()){// The script is available already. Run it ASAP when the event// loop gets a chance to spin.ProcessPendingRequestsAsync();}returnfalse;}// we now have a parser-inserted request that may or may not be still// loadingif(aElement->GetScriptDeferred()||request->IsModuleRequest()){// We don't want to run this yet.// If we come here, the script is a parser-created script and it has// the defer attribute but not the async attribute. Since a// a parser-inserted script is being run, we came here by the parser// running the script, which means the parser is still alive and the// parse is ongoing.NS_ASSERTION(mDocument->GetCurrentContentSink()||aElement->GetParserCreated()==FROM_PARSER_XSLT,"Non-XSLT Defer script on a document without an active parser; bug 592366.");AddDeferRequest(request);returnfalse;}if(aElement->GetParserCreated()==FROM_PARSER_XSLT){// Need to maintain order for XSLT-inserted scriptsNS_ASSERTION(!mParserBlockingRequest,"Parser-blocking scripts and XSLT scripts in the same doc!");request->mIsXSLT=true;mXSLTRequests.AppendElement(request);if(request->IsReadyToRun()){// The script is available already. Run it ASAP when the event// loop gets a chance to spin.ProcessPendingRequestsAsync();}returntrue;}if(request->IsReadyToRun()&&ReadyToExecuteParserBlockingScripts()){// The request has already been loaded and there are no pending style// sheets. If the script comes from the network stream, cheat for// performance reasons and avoid a trip through the event loop.if(aElement->GetParserCreated()==FROM_PARSER_NETWORK){returnProcessRequest(request)==NS_ERROR_HTMLPARSER_BLOCK;}// Otherwise, we've got a document.written script, make a trip through// the event loop to hide the preload effects from the scripts on the// Web page.NS_ASSERTION(!mParserBlockingRequest,"There can be only one parser-blocking script at a time");NS_ASSERTION(mXSLTRequests.isEmpty(),"Parser-blocking scripts and XSLT scripts in the same doc!");mParserBlockingRequest=request;ProcessPendingRequestsAsync();returntrue;}// The script hasn't loaded yet or there's a style sheet blocking it.// The script will be run when it loads or the style sheet loads.NS_ASSERTION(!mParserBlockingRequest,"There can be only one parser-blocking script at a time");NS_ASSERTION(mXSLTRequests.isEmpty(),"Parser-blocking scripts and XSLT scripts in the same doc!");mParserBlockingRequest=request;returntrue;}// inline script// Is this document sandboxed without 'allow-scripts'?if(mDocument->HasScriptsBlockedBySandbox()){returnfalse;}// Does CSP allow this inline script to run?if(!CSPAllowsInlineScript(aElement,mDocument)){returnfalse;}// Inline scripts ignore ther CORS mode and are always CORS_NONErequest=CreateLoadRequest(scriptKind,aElement,version,CORS_NONE,SRIMetadata());// SRI doesn't applyrequest->mJSVersion=version;request->mIsInline=true;request->mURI=mDocument->GetDocumentURI();request->mLineNo=aElement->GetScriptLineNumber();request->mProgress=ScriptLoadRequest::Progress::Loading_Source;request->mDataType=ScriptLoadRequest::DataType::Source;TRACE_FOR_TEST_BOOL(request->mElement,"scriptloader_load_source");CollectScriptTelemetry(nullptr,request);if(request->IsModuleRequest()){ModuleLoadRequest*modReq=request->AsModuleRequest();modReq->mBaseURL=mDocument->GetDocBaseURI();rv=CreateModuleScript(modReq);NS_ENSURE_SUCCESS(rv,false);StartFetchingModuleDependencies(modReq);if(aElement->GetScriptAsync()){mLoadingAsyncRequests.AppendElement(request);}else{AddDeferRequest(request);}returnfalse;}request->mProgress=ScriptLoadRequest::Progress::Ready;if(aElement->GetParserCreated()==FROM_PARSER_XSLT&&(!ReadyToExecuteParserBlockingScripts()||!mXSLTRequests.isEmpty())){// Need to maintain order for XSLT-inserted scriptsNS_ASSERTION(!mParserBlockingRequest,"Parser-blocking scripts and XSLT scripts in the same doc!");mXSLTRequests.AppendElement(request);returntrue;}if(aElement->GetParserCreated()==NOT_FROM_PARSER){NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),"A script-inserted script is inserted without an update batch?");nsContentUtils::AddScriptRunner(newScriptRequestProcessor(this,request));returnfalse;}if(aElement->GetParserCreated()==FROM_PARSER_NETWORK&&!ReadyToExecuteParserBlockingScripts()){NS_ASSERTION(!mParserBlockingRequest,"There can be only one parser-blocking script at a time");mParserBlockingRequest=request;NS_ASSERTION(mXSLTRequests.isEmpty(),"Parser-blocking scripts and XSLT scripts in the same doc!");returntrue;}// We now have a document.written inline script or we have an inline script// from the network but there is no style sheet that is blocking scripts.// Don't check for style sheets blocking scripts in the document.write// case to avoid style sheet network activity affecting when// document.write returns. It's not really necessary to do this if// there's no document.write currently on the call stack. However,// this way matches IE more closely than checking if document.write// is on the call stack.NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),"Not safe to run a parser-inserted script?");returnProcessRequest(request)==NS_ERROR_HTMLPARSER_BLOCK;}namespace{classNotifyOffThreadScriptLoadCompletedRunnable:publicRunnable{RefPtr<ScriptLoadRequest>mRequest;RefPtr<ScriptLoader>mLoader;RefPtr<DocGroup>mDocGroup;void*mToken;public:NotifyOffThreadScriptLoadCompletedRunnable(ScriptLoadRequest*aRequest,ScriptLoader*aLoader):Runnable("dom::NotifyOffThreadScriptLoadCompletedRunnable"),mRequest(aRequest),mLoader(aLoader),mDocGroup(aLoader->GetDocGroup()),mToken(nullptr){MOZ_ASSERT(NS_IsMainThread());}virtual~NotifyOffThreadScriptLoadCompletedRunnable();voidSetToken(void*aToken){MOZ_ASSERT(aToken&&!mToken);mToken=aToken;}staticvoidDispatch(already_AddRefed<NotifyOffThreadScriptLoadCompletedRunnable>&&aSelf){RefPtr<NotifyOffThreadScriptLoadCompletedRunnable>self=aSelf;RefPtr<DocGroup>docGroup=self->mDocGroup;docGroup->Dispatch("NotifyOffThreadScriptLoadCompletedRunnable",TaskCategory::Other,self.forget());}NS_DECL_NSIRUNNABLE};}/* anonymous namespace */nsresultScriptLoader::ProcessOffThreadRequest(ScriptLoadRequest*aRequest){MOZ_ASSERT(aRequest->mProgress==ScriptLoadRequest::Progress::Compiling);MOZ_ASSERT(!aRequest->mWasCompiledOMT);aRequest->mWasCompiledOMT=true;if(aRequest->IsModuleRequest()){MOZ_ASSERT(aRequest->mOffThreadToken);ModuleLoadRequest*request=aRequest->AsModuleRequest();nsresultrv=ProcessFetchedModuleSource(request);if(NS_FAILED(rv)){request->LoadFailed();}returnrv;}aRequest->SetReady();if(aRequest==mParserBlockingRequest){if(!ReadyToExecuteParserBlockingScripts()){// If not ready to execute scripts, schedule an async call to// ProcessPendingRequests to handle it.ProcessPendingRequestsAsync();returnNS_OK;}// Same logic as in top of ProcessPendingRequests.mParserBlockingRequest=nullptr;UnblockParser(aRequest);ProcessRequest(aRequest);mDocument->UnblockOnload(false);ContinueParserAsync(aRequest);returnNS_OK;}nsresultrv=ProcessRequest(aRequest);mDocument->UnblockOnload(false);returnrv;}NotifyOffThreadScriptLoadCompletedRunnable::~NotifyOffThreadScriptLoadCompletedRunnable(){if(MOZ_UNLIKELY(mRequest||mLoader)&&!NS_IsMainThread()){NS_ReleaseOnMainThread("NotifyOffThreadScriptLoadCompletedRunnable::mRequest",mRequest.forget());NS_ReleaseOnMainThread("NotifyOffThreadScriptLoadCompletedRunnable::mLoader",mLoader.forget());}}NS_IMETHODIMPNotifyOffThreadScriptLoadCompletedRunnable::Run(){MOZ_ASSERT(NS_IsMainThread());// We want these to be dropped on the main thread, once we return from this// function.RefPtr<ScriptLoadRequest>request=mRequest.forget();RefPtr<ScriptLoader>loader=mLoader.forget();request->mOffThreadToken=mToken;nsresultrv=loader->ProcessOffThreadRequest(request);returnrv;}staticvoidOffThreadScriptLoaderCallback(void*aToken,void*aCallbackData){RefPtr<NotifyOffThreadScriptLoadCompletedRunnable>aRunnable=dont_AddRef(static_cast<NotifyOffThreadScriptLoadCompletedRunnable*>(aCallbackData));aRunnable->SetToken(aToken);NotifyOffThreadScriptLoadCompletedRunnable::Dispatch(aRunnable.forget());}nsresultScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest*aRequest){MOZ_ASSERT_IF(!aRequest->IsModuleRequest(),aRequest->IsReadyToRun());MOZ_ASSERT(!aRequest->mWasCompiledOMT);// Don't off-thread compile inline scripts.if(aRequest->mIsInline){returnNS_ERROR_FAILURE;}nsCOMPtr<nsIScriptGlobalObject>globalObject=GetScriptGlobalObject();if(!globalObject){returnNS_ERROR_FAILURE;}AutoJSAPIjsapi;if(!jsapi.Init(globalObject)){returnNS_ERROR_FAILURE;}JSContext*cx=jsapi.cx();JS::Rooted<JSObject*>global(cx,globalObject->GetGlobalJSObject());JS::CompileOptionsoptions(cx);nsresultrv=FillCompileOptionsForRequest(jsapi,aRequest,global,&options);if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}size_tlen=aRequest->IsSource()?aRequest->mScriptText.length():aRequest->mScriptBytecode.length();if(!JS::CanCompileOffThread(cx,options,len)){returnNS_ERROR_FAILURE;}RefPtr<NotifyOffThreadScriptLoadCompletedRunnable>runnable=newNotifyOffThreadScriptLoadCompletedRunnable(aRequest,this);if(aRequest->IsModuleRequest()){MOZ_ASSERT(aRequest->IsSource());if(!JS::CompileOffThreadModule(cx,options,aRequest->mScriptText.begin(),aRequest->mScriptText.length(),OffThreadScriptLoaderCallback,static_cast<void*>(runnable))){returnNS_ERROR_OUT_OF_MEMORY;}}elseif(aRequest->IsSource()){if(!JS::CompileOffThread(cx,options,aRequest->mScriptText.begin(),aRequest->mScriptText.length(),OffThreadScriptLoaderCallback,static_cast<void*>(runnable))){returnNS_ERROR_OUT_OF_MEMORY;}}else{MOZ_ASSERT(aRequest->IsBytecode());if(!JS::DecodeOffThreadScript(cx,options,aRequest->mScriptBytecode,aRequest->mBytecodeOffset,OffThreadScriptLoaderCallback,static_cast<void*>(runnable))){returnNS_ERROR_OUT_OF_MEMORY;}}mDocument->BlockOnload();// Once the compilation is finished, an event would be added to the event loop// to call ScriptLoader::ProcessOffThreadRequest with the same request.aRequest->mProgress=ScriptLoadRequest::Progress::Compiling;Unused<<runnable.forget();returnNS_OK;}nsresultScriptLoader::CompileOffThreadOrProcessRequest(ScriptLoadRequest*aRequest){NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),"Processing requests when running scripts is unsafe.");NS_ASSERTION(!aRequest->mOffThreadToken,"Candidate for off-thread compile is already parsed off-thread");NS_ASSERTION(!aRequest->InCompilingStage(),"Candidate for off-thread compile is already in compiling stage.");nsresultrv=AttemptAsyncScriptCompile(aRequest);if(NS_SUCCEEDED(rv)){returnrv;}returnProcessRequest(aRequest);}SourceBufferHolderScriptLoader::GetScriptSource(ScriptLoadRequest*aRequest,nsAutoString&inlineData){// Return a SourceBufferHolder object holding the script's source text.// |inlineData| is used to hold the text for inline objects.// If there's no script text, we try to get it from the elementif(aRequest->mIsInline){// XXX This is inefficient - GetText makes multiple// copies.aRequest->mElement->GetScriptText(inlineData);returnSourceBufferHolder(inlineData.get(),inlineData.Length(),SourceBufferHolder::NoOwnership);}returnSourceBufferHolder(aRequest->mScriptText.begin(),aRequest->mScriptText.length(),SourceBufferHolder::NoOwnership);}nsresultScriptLoader::ProcessRequest(ScriptLoadRequest*aRequest){NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),"Processing requests when running scripts is unsafe.");NS_ASSERTION(aRequest->IsReadyToRun(),"Processing a request that is not ready to run.");NS_ENSURE_ARG(aRequest);if(aRequest->IsModuleRequest()&&!aRequest->AsModuleRequest()->mModuleScript){// There was an error parsing a module script. Nothing to do here.FireScriptAvailable(NS_ERROR_FAILURE,aRequest);returnNS_OK;}nsCOMPtr<nsINode>scriptElem=do_QueryInterface(aRequest->mElement);nsCOMPtr<nsIDocument>doc;if(!aRequest->mIsInline){doc=scriptElem->OwnerDoc();}nsCOMPtr<nsIScriptElement>oldParserInsertedScript;uint32_tparserCreated=aRequest->mElement->GetParserCreated();if(parserCreated){oldParserInsertedScript=mCurrentParserInsertedScript;mCurrentParserInsertedScript=aRequest->mElement;}aRequest->mElement->BeginEvaluating();FireScriptAvailable(NS_OK,aRequest);// The window may have gone away by this point, in which case there's no point// in trying to run the script.{// Try to perform a microtask checkpointnsAutoMicroTaskmt;}nsPIDOMWindowInner*pwin=mDocument->GetInnerWindow();boolrunScript=!!pwin;if(runScript){nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),scriptElem,NS_LITERAL_STRING("beforescriptexecute"),true,true,&runScript);}// Inner window could have gone away after firing beforescriptexecutepwin=mDocument->GetInnerWindow();if(!pwin){runScript=false;}nsresultrv=NS_OK;if(runScript){if(doc){doc->BeginEvaluatingExternalScript();}rv=EvaluateScript(aRequest);if(doc){doc->EndEvaluatingExternalScript();}nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),scriptElem,NS_LITERAL_STRING("afterscriptexecute"),true,false);}FireScriptEvaluated(rv,aRequest);aRequest->mElement->EndEvaluating();if(parserCreated){mCurrentParserInsertedScript=oldParserInsertedScript;}if(aRequest->mOffThreadToken){// The request was parsed off-main-thread, but the result of the off// thread parse was not actually needed to process the request// (disappearing window, some other error, ...). Finish the// request to avoid leaks in the JS engine.MOZ_ASSERT(!aRequest->IsModuleRequest());aRequest->MaybeCancelOffThreadScript();}// Free any source data, but keep the bytecode content as we might have to// save it later.aRequest->mScriptText.clearAndFree();returnrv;}voidScriptLoader::FireScriptAvailable(nsresultaResult,ScriptLoadRequest*aRequest){for(int32_ti=0;i<mObservers.Count();i++){nsCOMPtr<nsIScriptLoaderObserver>obs=mObservers[i];obs->ScriptAvailable(aResult,aRequest->mElement,aRequest->mIsInline,aRequest->mURI,aRequest->mLineNo);}aRequest->FireScriptAvailable(aResult);}voidScriptLoader::FireScriptEvaluated(nsresultaResult,ScriptLoadRequest*aRequest){for(int32_ti=0;i<mObservers.Count();i++){nsCOMPtr<nsIScriptLoaderObserver>obs=mObservers[i];obs->ScriptEvaluated(aResult,aRequest->mElement,aRequest->mIsInline);}aRequest->FireScriptEvaluated(aResult);}already_AddRefed<nsIScriptGlobalObject>ScriptLoader::GetScriptGlobalObject(){if(!mDocument){returnnullptr;}nsPIDOMWindowInner*pwin=mDocument->GetInnerWindow();if(!pwin){returnnullptr;}nsCOMPtr<nsIScriptGlobalObject>globalObject=do_QueryInterface(pwin);NS_ASSERTION(globalObject,"windows must be global objects");// and make sure we are setup for this type of script.nsresultrv=globalObject->EnsureScriptEnvironment();if(NS_FAILED(rv)){returnnullptr;}returnglobalObject.forget();}nsresultScriptLoader::FillCompileOptionsForRequest(constAutoJSAPI&jsapi,ScriptLoadRequest*aRequest,JS::Handle<JSObject*>aScopeChain,JS::CompileOptions*aOptions){// It's very important to use aRequest->mURI, not the final URI of the channel// aRequest ended up getting script data from, as the script filename.nsresultrv;nsContentUtils::GetWrapperSafeScriptFilename(mDocument,aRequest->mURI,aRequest->mURL,&rv);if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}if(mDocument){mDocument->NoteScriptTrackingStatus(aRequest->mURL,aRequest->IsTracking());}boolisScriptElement=!aRequest->IsModuleRequest()||aRequest->AsModuleRequest()->IsTopLevel();aOptions->setIntroductionType(isScriptElement?"scriptElement":"importedModule");aOptions->setFileAndLine(aRequest->mURL.get(),aRequest->mLineNo);aOptions->setVersion(JSVersion(aRequest->mJSVersion));aOptions->setIsRunOnce(true);aOptions->setNoScriptRval(true);if(aRequest->mHasSourceMapURL){aOptions->setSourceMapURL(aRequest->mSourceMapURL.get());}if(aRequest->mOriginPrincipal){nsIPrincipal*scriptPrin=nsContentUtils::ObjectPrincipal(aScopeChain);boolsubsumes=scriptPrin->Subsumes(aRequest->mOriginPrincipal);aOptions->setMutedErrors(!subsumes);}JSContext*cx=jsapi.cx();JS::Rooted<JS::Value>elementVal(cx);MOZ_ASSERT(aRequest->mElement);if(NS_SUCCEEDED(nsContentUtils::WrapNative(cx,aRequest->mElement,&elementVal,/* aAllowWrapping = */true))){MOZ_ASSERT(elementVal.isObject());aOptions->setElement(&elementVal.toObject());}returnNS_OK;}/* static */boolScriptLoader::ShouldCacheBytecode(ScriptLoadRequest*aRequest){usingmozilla::TimeStamp;usingmozilla::TimeDuration;// We need the nsICacheInfoChannel to exist to be able to open the alternate// data output stream. This pointer would only be non-null if the bytecode was// activated at the time the channel got created in StartLoad.if(!aRequest->mCacheInfo){LOG(("ScriptLoadRequest (%p): Cannot cache anything (cacheInfo = %p)",aRequest,aRequest->mCacheInfo.get()));returnfalse;}// Look at the preference to know which strategy (parameters) should be used// when the bytecode cache is enabled.int32_tstrategy=nsContentUtils::BytecodeCacheStrategy();// List of parameters used by the strategies.boolhasSourceLengthMin=false;boolhasFetchCountMin=false;boolhasTimeSinceLastFetched=false;size_tsourceLengthMin=100;int32_tfetchCountMin=5;TimeDurationtimeSinceLastFetched;LOG(("ScriptLoadRequest (%p): Bytecode-cache: strategy = %d.",aRequest,strategy));switch(strategy){case-2:{// Reader mode, keep requesting alternate data but no longer save it.LOG(("ScriptLoadRequest (%p): Bytecode-cache: Encoding disabled.",aRequest));returnfalse;}case-1:{// Eager mode, skip heuristics!hasSourceLengthMin=false;hasFetchCountMin=false;hasTimeSinceLastFetched=false;break;}default:case0:{hasSourceLengthMin=true;hasFetchCountMin=true;hasTimeSinceLastFetched=true;sourceLengthMin=1024;fetchCountMin=5;timeSinceLastFetched=TimeDuration::FromSeconds(72*3600);break;}// The following strategies are made-up to study what impact each parameter// has when compared to the default case.case1:{hasSourceLengthMin=true;hasFetchCountMin=true;hasTimeSinceLastFetched=false;sourceLengthMin=1024;fetchCountMin=5;break;}case2:{hasSourceLengthMin=true;hasFetchCountMin=false;hasTimeSinceLastFetched=true;sourceLengthMin=1024;timeSinceLastFetched=TimeDuration::FromSeconds(72*3600);break;}case3:{hasSourceLengthMin=false;hasFetchCountMin=true;hasTimeSinceLastFetched=true;fetchCountMin=5;timeSinceLastFetched=TimeDuration::FromSeconds(72*3600);break;}// The following strategies are made-up to study what impact each parameter// has individually.case4:{hasSourceLengthMin=false;hasFetchCountMin=false;hasTimeSinceLastFetched=true;timeSinceLastFetched=TimeDuration::FromSeconds(72*3600);break;}case5:{hasSourceLengthMin=false;hasFetchCountMin=true;hasTimeSinceLastFetched=false;fetchCountMin=5;break;}case6:{hasSourceLengthMin=true;hasFetchCountMin=false;hasTimeSinceLastFetched=false;sourceLengthMin=1024;break;}}// If the script is too small/large, do not attempt at creating a bytecode// cache for this script, as the overhead of parsing it might not be worth the// effort.if(hasSourceLengthMin&&aRequest->mScriptText.length()<sourceLengthMin){LOG(("ScriptLoadRequest (%p): Bytecode-cache: Script is too small.",aRequest));returnfalse;}// Check that we loaded the cache entry a few times before attempting any// bytecode-cache optimization, such that we do not waste time on entry which// are going to be dropped soon.if(hasFetchCountMin){int32_tfetchCount=0;if(NS_FAILED(aRequest->mCacheInfo->GetCacheTokenFetchCount(&fetchCount))){LOG(("ScriptLoadRequest (%p): Bytecode-cache: Cannot get fetchCount.",aRequest));returnfalse;}LOG(("ScriptLoadRequest (%p): Bytecode-cache: fetchCount = %d.",aRequest,fetchCount));if(fetchCount<fetchCountMin){returnfalse;}}// Check that the cache entry got accessed recently, before caching it.if(hasTimeSinceLastFetched){uint32_tlastFetched=0;if(NS_FAILED(aRequest->mCacheInfo->GetCacheTokenLastFetched(&lastFetched))){LOG(("ScriptLoadRequest (%p): Bytecode-cache: Cannot get lastFetched.",aRequest));returnfalse;}uint32_tnow=PR_Now()/PR_USEC_PER_SEC;if(now<lastFetched){LOG(("ScriptLoadRequest (%p): Bytecode-cache: (What?) lastFetched set in the future.",aRequest));returnfalse;}TimeDurationsince=TimeDuration::FromSeconds(now-lastFetched);LOG(("ScriptLoadRequest (%p): Bytecode-cache: lastFetched = %f sec. ago.",aRequest,since.ToSeconds()));if(since>=timeSinceLastFetched){returnfalse;}}LOG(("ScriptLoadRequest (%p): Bytecode-cache: Trigger encoding.",aRequest));returntrue;}nsresultScriptLoader::EvaluateScript(ScriptLoadRequest*aRequest){usingnamespacemozilla::Telemetry;MOZ_ASSERT(aRequest->IsReadyToRun());// We need a document to evaluate scripts.if(!mDocument){returnNS_ERROR_FAILURE;}nsCOMPtr<nsIContent>scriptContent(do_QueryInterface(aRequest->mElement));nsIDocument*ownerDoc=scriptContent->OwnerDoc();if(ownerDoc!=mDocument){// Willful violation of HTML5 as of 2010-12-01returnNS_ERROR_FAILURE;}// Report telemetry results of the number of scripts evaluated.mDocument->SetDocumentIncCounter(IncCounter::eIncCounter_ScriptTag);// Get the script-type to be used by this element.NS_ASSERTION(scriptContent,"no content - what is default script-type?");nsCOMPtr<nsIScriptGlobalObject>globalObject=GetScriptGlobalObject();if(!globalObject){returnNS_ERROR_FAILURE;}// Make sure context is a strong reference since we access it after// we've executed a script, which may cause all other references to// the context to go away.nsCOMPtr<nsIScriptContext>context=globalObject->GetScriptContext();if(!context){returnNS_ERROR_FAILURE;}JSVersionversion=JSVersion(aRequest->mJSVersion);if(version==JSVERSION_UNKNOWN){returnNS_OK;}// New script entry point required, due to the "Create a script" sub-step of// http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-blocknsAutoMicroTaskmt;AutoEntryScriptaes(globalObject,"<script> element",true);JS::Rooted<JSObject*>global(aes.cx(),globalObject->GetGlobalJSObject());boololdProcessingScriptTag=context->GetProcessingScriptTag();context->SetProcessingScriptTag(true);nsresultrv;{// Update our current script.AutoCurrentScriptUpdaterscriptUpdater(this,aRequest->mElement);if(aRequest->IsModuleRequest()){// When a module is already loaded, it is not feched a second time and the// mDataType of the request might remain set to DataType::Unknown.MOZ_ASSERT(!aRequest->IsBytecode());LOG(("ScriptLoadRequest (%p): Evaluate Module",aRequest));ModuleLoadRequest*request=aRequest->AsModuleRequest();MOZ_ASSERT(request->mModuleScript);MOZ_ASSERT(!request->mOffThreadToken);ModuleScript*ms=request->mModuleScript;MOZ_ASSERT(!ms->IsUninstantiated());if(ms->InstantiationFailed()){JS::Rooted<JS::Value>exception(aes.cx(),ms->Exception());JS_SetPendingException(aes.cx(),exception);rv=NS_ERROR_FAILURE;}else{JS::Rooted<JSObject*>module(aes.cx(),ms->ModuleRecord());MOZ_ASSERT(module);rv=nsJSUtils::ModuleEvaluation(aes.cx(),module);}aRequest->mCacheInfo=nullptr;}else{JS::CompileOptionsoptions(aes.cx());rv=FillCompileOptionsForRequest(aes,aRequest,global,&options);if(NS_SUCCEEDED(rv)){if(aRequest->IsBytecode()){TRACE_FOR_TEST(aRequest->mElement,"scriptloader_execute");nsJSUtils::ExecutionContextexec(aes.cx(),global);if(aRequest->mOffThreadToken){LOG(("ScriptLoadRequest (%p): Decode Bytecode & Join and Execute",aRequest));AutoTimer<DOM_SCRIPT_OFF_THREAD_DECODE_EXEC_MS>timer;rv=exec.DecodeJoinAndExec(&aRequest->mOffThreadToken);}else{LOG(("ScriptLoadRequest (%p): Decode Bytecode and Execute",aRequest));AutoTimer<DOM_SCRIPT_MAIN_THREAD_DECODE_EXEC_MS>timer;rv=exec.DecodeAndExec(options,aRequest->mScriptBytecode,aRequest->mBytecodeOffset);}// We do not expect to be saving anything when we already have some// bytecode.MOZ_ASSERT(!aRequest->mCacheInfo);}else{MOZ_ASSERT(aRequest->IsSource());JS::Rooted<JSScript*>script(aes.cx());boolencodeBytecode=ShouldCacheBytecode(aRequest);TimeStampstart;if(Telemetry::CanRecordExtended()){// Only record telemetry for scripts which are above the threshold.if(aRequest->mCacheInfo&&aRequest->mScriptText.length()>=1024){start=TimeStamp::Now();}}{nsJSUtils::ExecutionContextexec(aes.cx(),global);exec.SetEncodeBytecode(encodeBytecode);TRACE_FOR_TEST(aRequest->mElement,"scriptloader_execute");if(aRequest->mOffThreadToken){// Off-main-thread parsing.LOG(("ScriptLoadRequest (%p): Join (off-thread parsing) and Execute",aRequest));rv=exec.JoinAndExec(&aRequest->mOffThreadToken,&script);if(start){AccumulateTimeDelta(encodeBytecode?DOM_SCRIPT_OFF_THREAD_PARSE_ENCODE_EXEC_MS:DOM_SCRIPT_OFF_THREAD_PARSE_EXEC_MS,start);}}else{// Main thread parsing (inline and small scripts)LOG(("ScriptLoadRequest (%p): Compile And Exec",aRequest));nsAutoStringinlineData;SourceBufferHoldersrcBuf=GetScriptSource(aRequest,inlineData);rv=exec.CompileAndExec(options,srcBuf,&script);if(start){AccumulateTimeDelta(encodeBytecode?DOM_SCRIPT_MAIN_THREAD_PARSE_ENCODE_EXEC_MS:DOM_SCRIPT_MAIN_THREAD_PARSE_EXEC_MS,start);}}}// Queue the current script load request to later save the bytecode.if(script&&encodeBytecode){aRequest->mScript=script;HoldJSObjects(aRequest);TRACE_FOR_TEST(aRequest->mElement,"scriptloader_encode");MOZ_ASSERT(aRequest->mBytecodeOffset==aRequest->mScriptBytecode.length());RegisterForBytecodeEncoding(aRequest);}else{LOG(("ScriptLoadRequest (%p): Bytecode-cache: disabled (rv = %X, script = %p)",aRequest,unsigned(rv),script.get()));TRACE_FOR_TEST_NONE(aRequest->mElement,"scriptloader_no_encode");aRequest->mCacheInfo=nullptr;}}}}// Even if we are not saving the bytecode of the current script, we have// to trigger the encoding of the bytecode, as the current script can// call functions of a script for which we are recording the bytecode.MaybeTriggerBytecodeEncoding();}context->SetProcessingScriptTag(oldProcessingScriptTag);returnrv;}voidScriptLoader::RegisterForBytecodeEncoding(ScriptLoadRequest*aRequest){MOZ_ASSERT(aRequest->mCacheInfo);MOZ_ASSERT(aRequest->mScript);mBytecodeEncodingQueue.AppendElement(aRequest);}voidScriptLoader::LoadEventFired(){mLoadEventFired=true;MaybeTriggerBytecodeEncoding();}voidScriptLoader::MaybeTriggerBytecodeEncoding(){// We wait for the load event to be fired before saving the bytecode of// any script to the cache. It is quite common to have load event// listeners trigger more JavaScript execution, that we want to save as// part of this start-up bytecode cache.if(!mLoadEventFired){return;}// No need to fire any event if there is no bytecode to be saved.if(mBytecodeEncodingQueue.isEmpty()){return;}// Wait until all scripts are loaded before saving the bytecode, such that// we capture most of the intialization of the page.if(HasPendingRequests()){return;}// Create a new runnable dedicated to encoding the content of the bytecode of// all enqueued scripts when the document is idle. In case of failure, we// give-up on encoding the bytecode.nsCOMPtr<nsIRunnable>encoder=NewRunnableMethod("ScriptLoader::EncodeBytecode",this,&ScriptLoader::EncodeBytecode);if(NS_FAILED(NS_IdleDispatchToCurrentThread(encoder.forget()))){GiveUpBytecodeEncoding();}}voidScriptLoader::EncodeBytecode(){// If any script got added in the previous loop cycle, wait until all// remaining script executions are completed, such that we capture most of// the initialization.if(HasPendingRequests()){return;}nsCOMPtr<nsIScriptGlobalObject>globalObject=GetScriptGlobalObject();if(!globalObject){GiveUpBytecodeEncoding();return;}nsCOMPtr<nsIScriptContext>context=globalObject->GetScriptContext();if(!context){GiveUpBytecodeEncoding();return;}Telemetry::AutoTimer<Telemetry::DOM_SCRIPT_ENCODING_MS_PER_DOCUMENT>timer;AutoEntryScriptaes(globalObject,"encode bytecode",true);RefPtr<ScriptLoadRequest>request;while(!mBytecodeEncodingQueue.isEmpty()){request=mBytecodeEncodingQueue.StealFirst();EncodeRequestBytecode(aes.cx(),request);request->mScriptBytecode.clearAndFree();request->DropBytecodeCacheReferences();}}voidScriptLoader::EncodeRequestBytecode(JSContext*aCx,ScriptLoadRequest*aRequest){usingnamespacemozilla::Telemetry;nsresultrv=NS_OK;MOZ_ASSERT(aRequest->mCacheInfo);autobytecodeFailed=mozilla::MakeScopeExit([&](){TRACE_FOR_TEST_NONE(aRequest->mElement,"scriptloader_bytecode_failed");});JS::RootedScriptscript(aCx,aRequest->mScript);if(!JS::FinishIncrementalEncoding(aCx,script,aRequest->mScriptBytecode)){LOG(("ScriptLoadRequest (%p): Cannot serialize bytecode",aRequest));AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::EncodingFailure);return;}if(aRequest->mScriptBytecode.length()>=UINT32_MAX){LOG(("ScriptLoadRequest (%p): Bytecode cache is too large to be decoded correctly.",aRequest));AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::BufferTooLarge);return;}// Open the output stream to the cache entry alternate data storage. This// might fail if the stream is already open by another request, in which// case, we just ignore the current one.nsCOMPtr<nsIOutputStream>output;rv=aRequest->mCacheInfo->OpenAlternativeOutputStream(nsContentUtils::JSBytecodeMimeType(),getter_AddRefs(output));if(NS_FAILED(rv)){LOG(("ScriptLoadRequest (%p): Cannot open bytecode cache (rv = %X, output = %p)",aRequest,unsigned(rv),output.get()));AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::OpenFailure);return;}MOZ_ASSERT(output);autocloseOutStream=mozilla::MakeScopeExit([&](){nsresultrv=output->Close();LOG(("ScriptLoadRequest (%p): Closing (rv = %X)",aRequest,unsigned(rv)));if(NS_FAILED(rv)){AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::CloseFailure);}});uint32_tn;rv=output->Write(reinterpret_cast<char*>(aRequest->mScriptBytecode.begin()),aRequest->mScriptBytecode.length(),&n);LOG(("ScriptLoadRequest (%p): Write bytecode cache (rv = %X, length = %u, written = %u)",aRequest,unsigned(rv),unsigned(aRequest->mScriptBytecode.length()),n));if(NS_FAILED(rv)){AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::WriteFailure);return;}bytecodeFailed.release();TRACE_FOR_TEST_NONE(aRequest->mElement,"scriptloader_bytecode_saved");AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::EncodingSuccess);}voidScriptLoader::GiveUpBytecodeEncoding(){// Ideally we prefer to properly end the incremental encoder, such that we// would not keep a large buffer around. If we cannot, we fallback on the// removal of all request from the current list and these large buffers would// be removed at the same time as the source object.nsCOMPtr<nsIScriptGlobalObject>globalObject=GetScriptGlobalObject();Maybe<AutoEntryScript>aes;if(globalObject){nsCOMPtr<nsIScriptContext>context=globalObject->GetScriptContext();if(context){aes.emplace(globalObject,"give-up bytecode encoding",true);}}while(!mBytecodeEncodingQueue.isEmpty()){RefPtr<ScriptLoadRequest>request=mBytecodeEncodingQueue.StealFirst();LOG(("ScriptLoadRequest (%p): Cannot serialize bytecode",request.get()));TRACE_FOR_TEST_NONE(request->mElement,"scriptloader_bytecode_failed");if(aes.isSome()){JS::RootedScriptscript(aes->cx(),request->mScript);Unused<<JS::FinishIncrementalEncoding(aes->cx(),script,request->mScriptBytecode);}request->mScriptBytecode.clearAndFree();request->DropBytecodeCacheReferences();}}boolScriptLoader::HasPendingRequests(){returnmParserBlockingRequest||!mXSLTRequests.isEmpty()||!mLoadedAsyncRequests.isEmpty()||!mNonAsyncExternalScriptInsertedRequests.isEmpty()||!mDeferRequests.isEmpty()||!mPendingChildLoaders.IsEmpty();}voidScriptLoader::ProcessPendingRequestsAsync(){if(HasPendingRequests()){nsCOMPtr<nsIRunnable>task=NewRunnableMethod("dom::ScriptLoader::ProcessPendingRequests",this,&ScriptLoader::ProcessPendingRequests);if(mDocument){mDocument->Dispatch("ScriptLoader",TaskCategory::Other,task.forget());}else{NS_DispatchToCurrentThread(task.forget());}}}voidScriptLoader::ProcessPendingRequests(){RefPtr<ScriptLoadRequest>request;if(mParserBlockingRequest&&mParserBlockingRequest->IsReadyToRun()&&ReadyToExecuteParserBlockingScripts()){request.swap(mParserBlockingRequest);UnblockParser(request);ProcessRequest(request);if(request->mWasCompiledOMT){mDocument->UnblockOnload(false);}ContinueParserAsync(request);}while(ReadyToExecuteParserBlockingScripts()&&!mXSLTRequests.isEmpty()&&mXSLTRequests.getFirst()->IsReadyToRun()){request=mXSLTRequests.StealFirst();ProcessRequest(request);}while(ReadyToExecuteScripts()&&!mLoadedAsyncRequests.isEmpty()){request=mLoadedAsyncRequests.StealFirst();if(request->IsModuleRequest()){ProcessRequest(request);}else{CompileOffThreadOrProcessRequest(request);}}while(ReadyToExecuteScripts()&&!mNonAsyncExternalScriptInsertedRequests.isEmpty()&&mNonAsyncExternalScriptInsertedRequests.getFirst()->IsReadyToRun()){// Violate the HTML5 spec and execute these in the insertion order in// order to make LABjs and the "order" plug-in for RequireJS work with// their Gecko-sniffed code path. See// http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.htmlrequest=mNonAsyncExternalScriptInsertedRequests.StealFirst();ProcessRequest(request);}if(mDocumentParsingDone&&mXSLTRequests.isEmpty()){while(ReadyToExecuteScripts()&&!mDeferRequests.isEmpty()&&mDeferRequests.getFirst()->IsReadyToRun()){request=mDeferRequests.StealFirst();ProcessRequest(request);}}while(!mPendingChildLoaders.IsEmpty()&&ReadyToExecuteParserBlockingScripts()){RefPtr<ScriptLoader>child=mPendingChildLoaders[0];mPendingChildLoaders.RemoveElementAt(0);child->RemoveParserBlockingScriptExecutionBlocker();}if(mDocumentParsingDone&&mDocument&&!mParserBlockingRequest&&mNonAsyncExternalScriptInsertedRequests.isEmpty()&&mXSLTRequests.isEmpty()&&mDeferRequests.isEmpty()&&MaybeRemovedDeferRequests()){returnProcessPendingRequests();}if(mDocumentParsingDone&&mDocument&&!mParserBlockingRequest&&mLoadingAsyncRequests.isEmpty()&&mLoadedAsyncRequests.isEmpty()&&mNonAsyncExternalScriptInsertedRequests.isEmpty()&&mXSLTRequests.isEmpty()&&mDeferRequests.isEmpty()){// No more pending scripts; time to unblock onload.// OK to unblock onload synchronously here, since callers must be// prepared for the world changing anyway.mDocumentParsingDone=false;mDocument->UnblockOnload(true);}}boolScriptLoader::ReadyToExecuteParserBlockingScripts(){// Make sure the SelfReadyToExecuteParserBlockingScripts check is first, so// that we don't block twice on an ancestor.if(!SelfReadyToExecuteParserBlockingScripts()){returnfalse;}for(nsIDocument*doc=mDocument;doc;doc=doc->GetParentDocument()){ScriptLoader*ancestor=doc->ScriptLoader();if(!ancestor->SelfReadyToExecuteParserBlockingScripts()&&ancestor->AddPendingChildLoader(this)){AddParserBlockingScriptExecutionBlocker();returnfalse;}}returntrue;}/* static */nsresultScriptLoader::ConvertToUTF16(nsIChannel*aChannel,constuint8_t*aData,uint32_taLength,constnsAString&aHintCharset,nsIDocument*aDocument,char16_t*&aBufOut,size_t&aLengthOut){if(!aLength){aBufOut=nullptr;aLengthOut=0;returnNS_OK;}autodata=MakeSpan(aData,aLength);// The encoding info precedence is as follows from high to low:// The BOM// HTTP Content-Type (if name recognized)// charset attribute (if name recognized)// The encoding of the documentUniquePtr<Decoder>unicodeDecoder;constEncoding*encoding;size_tbomLength;Tie(encoding,bomLength)=Encoding::ForBOM(data);if(encoding){unicodeDecoder=encoding->NewDecoderWithBOMRemoval();}if(!unicodeDecoder&&aChannel){nsAutoCStringlabel;if(NS_SUCCEEDED(aChannel->GetContentCharset(label))&&(encoding=Encoding::ForLabel(label))){unicodeDecoder=encoding->NewDecoderWithoutBOMHandling();}}if(!unicodeDecoder&&(encoding=Encoding::ForLabel(aHintCharset))){unicodeDecoder=encoding->NewDecoderWithoutBOMHandling();}if(!unicodeDecoder&&aDocument){unicodeDecoder=aDocument->GetDocumentCharacterSet()->NewDecoderWithoutBOMHandling();}if(!unicodeDecoder){// Curiously, there are various callers that don't pass aDocument. The// fallback in the old code was ISO-8859-1, which behaved like// windows-1252.unicodeDecoder=WINDOWS_1252_ENCODING->NewDecoderWithoutBOMHandling();}CheckedInt<size_t>unicodeLength=unicodeDecoder->MaxUTF16BufferLength(aLength);if(!unicodeLength.isValid()){returnNS_ERROR_OUT_OF_MEMORY;}aBufOut=static_cast<char16_t*>(js_malloc(unicodeLength.value()*sizeof(char16_t)));if(!aBufOut){aLengthOut=0;returnNS_ERROR_OUT_OF_MEMORY;}uint32_tresult;size_tread;size_twritten;boolhadErrors;Tie(result,read,written,hadErrors)=unicodeDecoder->DecodeToUTF16(data,MakeSpan(aBufOut,unicodeLength.value()),true);MOZ_ASSERT(result==kInputEmpty);MOZ_ASSERT(read==aLength);MOZ_ASSERT(written<=unicodeLength.value());Unused<<hadErrors;aLengthOut=written;nsAutoCStringcharset;unicodeDecoder->Encoding()->Name(charset);mozilla::Telemetry::Accumulate(mozilla::Telemetry::DOM_SCRIPT_SRC_ENCODING,charset);returnNS_OK;}nsresultScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader*aLoader,ScriptLoadRequest*aRequest,nsresultaChannelStatus,nsresultaSRIStatus,mozilla::dom::SRICheckDataVerifier*aSRIDataVerifier){NS_ASSERTION(aRequest,"null request in stream complete handler");NS_ENSURE_TRUE(aRequest,NS_ERROR_FAILURE);nsCOMPtr<nsIRequest>channelRequest;aLoader->GetRequest(getter_AddRefs(channelRequest));nsCOMPtr<nsIChannel>channel;channel=do_QueryInterface(channelRequest);nsresultrv=NS_OK;if(!aRequest->mIntegrity.IsEmpty()&&NS_SUCCEEDED((rv=aSRIStatus))){MOZ_ASSERT(aSRIDataVerifier);MOZ_ASSERT(mReporter);nsAutoCStringsourceUri;if(mDocument&&mDocument->GetDocumentURI()){mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);}rv=aSRIDataVerifier->Verify(aRequest->mIntegrity,channel,sourceUri,mReporter);if(channelRequest){mReporter->FlushReportsToConsole(nsContentUtils::GetInnerWindowID(channelRequest));}else{mReporter->FlushConsoleReports(mDocument);}if(NS_FAILED(rv)){rv=NS_ERROR_SRI_CORRUPT;}}else{nsCOMPtr<nsILoadInfo>loadInfo=channel->GetLoadInfo();if(loadInfo&&loadInfo->GetEnforceSRI()){MOZ_LOG(SRILogHelper::GetSriLog(),mozilla::LogLevel::Debug,("ScriptLoader::OnStreamComplete, required SRI not found"));nsCOMPtr<nsIContentSecurityPolicy>csp;loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));nsAutoCStringviolationURISpec;mDocument->GetDocumentURI()->GetAsciiSpec(violationURISpec);uint32_tlineNo=aRequest->mElement?aRequest->mElement->GetScriptLineNumber():0;csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT,NS_ConvertUTF8toUTF16(violationURISpec),EmptyString(),lineNo,EmptyString(),EmptyString());rv=NS_ERROR_SRI_CORRUPT;}}boolsriOk=NS_SUCCEEDED(rv);// If we are loading from source, save the computed SRI hash or a dummy SRI// hash in case we are going to save the bytecode of this script in the cache.if(sriOk&&aRequest->IsSource()){MOZ_ASSERT(aRequest->mScriptBytecode.empty());// If the integrity metadata does not correspond to a valid hash function,// IsComplete would be false.if(!aRequest->mIntegrity.IsEmpty()&&aSRIDataVerifier->IsComplete()){// Encode the SRI computed hash.uint32_tlen=aSRIDataVerifier->DataSummaryLength();if(!aRequest->mScriptBytecode.growBy(len)){returnNS_ERROR_OUT_OF_MEMORY;}aRequest->mBytecodeOffset=len;DebugOnly<nsresult>res=aSRIDataVerifier->ExportDataSummary(aRequest->mScriptBytecode.length(),aRequest->mScriptBytecode.begin());MOZ_ASSERT(NS_SUCCEEDED(res));}else{// Encode a dummy SRI hash.uint32_tlen=SRICheckDataVerifier::EmptyDataSummaryLength();if(!aRequest->mScriptBytecode.growBy(len)){returnNS_ERROR_OUT_OF_MEMORY;}aRequest->mBytecodeOffset=len;DebugOnly<nsresult>res=SRICheckDataVerifier::ExportEmptyDataSummary(aRequest->mScriptBytecode.length(),aRequest->mScriptBytecode.begin());MOZ_ASSERT(NS_SUCCEEDED(res));}// Verify that the exported and predicted length correspond.mozilla::DebugOnly<uint32_t>srilen;MOZ_ASSERT(NS_SUCCEEDED(SRICheckDataVerifier::DataSummaryLength(aRequest->mScriptBytecode.length(),aRequest->mScriptBytecode.begin(),&srilen)));MOZ_ASSERT(srilen==aRequest->mBytecodeOffset);}if(sriOk){rv=PrepareLoadedRequest(aRequest,aLoader,aChannelStatus);}if(NS_FAILED(rv)){if(sriOk&&aRequest->mElement){uint32_tlineNo=aRequest->mElement->GetScriptLineNumber();nsAutoStringurl;if(aRequest->mURI){AppendUTF8toUTF16(aRequest->mURI->GetSpecOrDefault(),url);}constchar16_t*params[]={url.get()};nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,NS_LITERAL_CSTRING("Script Loader"),mDocument,nsContentUtils::eDOM_PROPERTIES,"ScriptSourceLoadFailed",params,ArrayLength(params),nullptr,EmptyString(),lineNo);}/* * Handle script not loading error because source was a tracking URL. * We make a note of this script node by including it in a dedicated * array of blocked tracking nodes under its parent document. */if(rv==NS_ERROR_TRACKING_URI){nsCOMPtr<nsIContent>cont=do_QueryInterface(aRequest->mElement);mDocument->AddBlockedTrackingNode(cont);}if(aChannelStatus==NS_BINDING_RETARGETED){// When loading bytecode, we verify the SRI hash. If it does not match the// one from the document we restart the load, forcing us to load the// source instead. This test makes sure we do not remove the current// request from the following lists when we restart the load.}elseif(aRequest->mIsDefer){MOZ_ASSERT_IF(aRequest->IsModuleRequest(),aRequest->AsModuleRequest()->IsTopLevel());if(aRequest->isInList()){RefPtr<ScriptLoadRequest>req=mDeferRequests.Steal(aRequest);FireScriptAvailable(rv,req);}}elseif(aRequest->mIsAsync){MOZ_ASSERT_IF(aRequest->IsModuleRequest(),aRequest->AsModuleRequest()->IsTopLevel());if(aRequest->isInList()){RefPtr<ScriptLoadRequest>req=mLoadingAsyncRequests.Steal(aRequest);FireScriptAvailable(rv,req);}}elseif(aRequest->mIsNonAsyncScriptInserted){if(aRequest->isInList()){RefPtr<ScriptLoadRequest>req=mNonAsyncExternalScriptInsertedRequests.Steal(aRequest);FireScriptAvailable(rv,req);}}elseif(aRequest->mIsXSLT){if(aRequest->isInList()){RefPtr<ScriptLoadRequest>req=mXSLTRequests.Steal(aRequest);FireScriptAvailable(rv,req);}}elseif(aRequest->IsModuleRequest()){ModuleLoadRequest*modReq=aRequest->AsModuleRequest();MOZ_ASSERT(!modReq->IsTopLevel());MOZ_ASSERT(!modReq->isInList());modReq->Cancel();FireScriptAvailable(rv,aRequest);}elseif(mParserBlockingRequest==aRequest){MOZ_ASSERT(!aRequest->isInList());mParserBlockingRequest=nullptr;UnblockParser(aRequest);// Ensure that we treat aRequest->mElement as our current parser-inserted// script while firing onerror on it.MOZ_ASSERT(aRequest->mElement->GetParserCreated());nsCOMPtr<nsIScriptElement>oldParserInsertedScript=mCurrentParserInsertedScript;mCurrentParserInsertedScript=aRequest->mElement;FireScriptAvailable(rv,aRequest);ContinueParserAsync(aRequest);mCurrentParserInsertedScript=oldParserInsertedScript;}else{mPreloads.RemoveElement(aRequest,PreloadRequestComparator());}}// Process our request and/or any pending onesProcessPendingRequests();returnNS_OK;}voidScriptLoader::UnblockParser(ScriptLoadRequest*aParserBlockingRequest){aParserBlockingRequest->mElement->UnblockParser();}voidScriptLoader::ContinueParserAsync(ScriptLoadRequest*aParserBlockingRequest){aParserBlockingRequest->mElement->ContinueParserAsync();}uint32_tScriptLoader::NumberOfProcessors(){if(mNumberOfProcessors>0)returnmNumberOfProcessors;int32_tnumProcs=PR_GetNumberOfProcessors();if(numProcs>0)mNumberOfProcessors=numProcs;returnmNumberOfProcessors;}voidScriptLoader::MaybeMoveToLoadedList(ScriptLoadRequest*aRequest){MOZ_ASSERT(aRequest->IsReadyToRun());// If it's async, move it to the loaded list. aRequest->mIsAsync really// _should_ be in a list, but the consequences if it's not are bad enough we// want to avoid trying to move it if it's not.if(aRequest->mIsAsync){MOZ_ASSERT(aRequest->isInList());if(aRequest->isInList()){RefPtr<ScriptLoadRequest>req=mLoadingAsyncRequests.Steal(aRequest);mLoadedAsyncRequests.AppendElement(req);}}}nsresultScriptLoader::PrepareLoadedRequest(ScriptLoadRequest*aRequest,nsIIncrementalStreamLoader*aLoader,nsresultaStatus){if(NS_FAILED(aStatus)){returnaStatus;}if(aRequest->IsCanceled()){returnNS_BINDING_ABORTED;}MOZ_ASSERT(aRequest->IsLoading());CollectScriptTelemetry(aLoader,aRequest);// If we don't have a document, then we need to abort further// evaluation.if(!mDocument){returnNS_ERROR_NOT_AVAILABLE;}// If the load returned an error page, then we need to abortnsCOMPtr<nsIRequest>req;nsresultrv=aLoader->GetRequest(getter_AddRefs(req));NS_ASSERTION(req,"StreamLoader's request went away prematurely");NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIHttpChannel>httpChannel=do_QueryInterface(req);if(httpChannel){boolrequestSucceeded;rv=httpChannel->GetRequestSucceeded(&requestSucceeded);if(NS_SUCCEEDED(rv)&&!requestSucceeded){returnNS_ERROR_NOT_AVAILABLE;}nsAutoCStringsourceMapURL;rv=httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("SourceMap"),sourceMapURL);if(NS_FAILED(rv)){rv=httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"),sourceMapURL);}if(NS_SUCCEEDED(rv)){aRequest->mHasSourceMapURL=true;aRequest->mSourceMapURL=NS_ConvertUTF8toUTF16(sourceMapURL);}if(httpChannel->GetIsTrackingResource()){aRequest->SetIsTracking();}}nsCOMPtr<nsIChannel>channel=do_QueryInterface(req);// If this load was subject to a CORS check; don't flag it with a// separate origin principal, so that it will treat our document's// principal as the origin principalif(aRequest->mCORSMode==CORS_NONE){rv=nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(channel,getter_AddRefs(aRequest->mOriginPrincipal));NS_ENSURE_SUCCESS(rv,rv);}// This assertion could fire errorously if we ran out of memory when// inserting the request in the array. However it's an unlikely case// so if you see this assertion it is likely something else that is// wrong, especially if you see it more than once.NS_ASSERTION(mDeferRequests.Contains(aRequest)||mLoadingAsyncRequests.Contains(aRequest)||mNonAsyncExternalScriptInsertedRequests.Contains(aRequest)||mXSLTRequests.Contains(aRequest)||(aRequest->IsModuleRequest()&&!aRequest->AsModuleRequest()->IsTopLevel()&&!aRequest->isInList())||mPreloads.Contains(aRequest,PreloadRequestComparator())||mParserBlockingRequest,"aRequest should be pending!");if(aRequest->IsModuleRequest()){MOZ_ASSERT(aRequest->IsSource());ModuleLoadRequest*request=aRequest->AsModuleRequest();// When loading a module, only responses with a JavaScript MIME type are// acceptable.nsAutoCStringmimeType;channel->GetContentType(mimeType);NS_ConvertUTF8toUTF16typeString(mimeType);if(!nsContentUtils::IsJavascriptMIMEType(typeString)){returnNS_ERROR_FAILURE;}channel->GetURI(getter_AddRefs(request->mBaseURL));// Attempt to compile off main thread.rv=AttemptAsyncScriptCompile(request);if(NS_SUCCEEDED(rv)){returnrv;}// Otherwise compile it right away and start fetching descendents.returnProcessFetchedModuleSource(request);}// The script is now loaded and ready to run.aRequest->SetReady();// If this is currently blocking the parser, attempt to compile it off-main-thread.if(aRequest==mParserBlockingRequest&&NumberOfProcessors()>1){MOZ_ASSERT(!aRequest->IsModuleRequest());nsresultrv=AttemptAsyncScriptCompile(aRequest);if(rv==NS_OK){MOZ_ASSERT(aRequest->mProgress==ScriptLoadRequest::Progress::Compiling,"Request should be off-thread compiling now.");returnNS_OK;}// If off-thread compile errored, return the error.if(rv!=NS_ERROR_FAILURE){returnrv;}// If off-thread compile was rejected, continue with regular processing.}MaybeMoveToLoadedList(aRequest);returnNS_OK;}voidScriptLoader::ParsingComplete(boolaTerminated){if(mDeferEnabled){// Have to check because we apparently get ParsingComplete// without BeginDeferringScripts in some casesmDocumentParsingDone=true;}mDeferEnabled=false;if(aTerminated){mDeferRequests.Clear();mLoadingAsyncRequests.Clear();mLoadedAsyncRequests.Clear();mNonAsyncExternalScriptInsertedRequests.Clear();mXSLTRequests.Clear();if(mParserBlockingRequest){mParserBlockingRequest->Cancel();mParserBlockingRequest=nullptr;}}// Have to call this even if aTerminated so we'll correctly unblock// onload and all.ProcessPendingRequests();}voidScriptLoader::PreloadURI(nsIURI*aURI,constnsAString&aCharset,constnsAString&aType,constnsAString&aCrossOrigin,constnsAString&aIntegrity,boolaScriptFromHead,constmozilla::net::ReferrerPolicyaReferrerPolicy){NS_ENSURE_TRUE_VOID(mDocument);// Check to see if scripts has been turned off.if(!mEnabled||!mDocument->IsScriptEnabled()){return;}// TODO: Preload module scripts.if(ModuleScriptsEnabled()&&aType.LowerCaseEqualsASCII("module")){return;}SRIMetadatasriMetadata;if(!aIntegrity.IsEmpty()){MOZ_LOG(SRILogHelper::GetSriLog(),mozilla::LogLevel::Debug,("ScriptLoader::PreloadURI, integrity=%s",NS_ConvertUTF16toUTF8(aIntegrity).get()));nsAutoCStringsourceUri;if(mDocument->GetDocumentURI()){mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);}SRICheck::IntegrityMetadata(aIntegrity,sourceUri,mReporter,&sriMetadata);}RefPtr<ScriptLoadRequest>request=CreateLoadRequest(ScriptKind::Classic,nullptr,0,Element::StringToCORSMode(aCrossOrigin),sriMetadata);request->mURI=aURI;request->mIsInline=false;request->mReferrerPolicy=aReferrerPolicy;request->mScriptFromHead=aScriptFromHead;nsresultrv=StartLoad(request);if(NS_FAILED(rv)){return;}PreloadInfo*pi=mPreloads.AppendElement();pi->mRequest=request;pi->mCharset=aCharset;}voidScriptLoader::AddDeferRequest(ScriptLoadRequest*aRequest){aRequest->mIsDefer=true;mDeferRequests.AppendElement(aRequest);if(mDeferEnabled&&aRequest==mDeferRequests.getFirst()&&mDocument&&!mBlockingDOMContentLoaded){MOZ_ASSERT(mDocument->GetReadyStateEnum()==nsIDocument::READYSTATE_LOADING);mBlockingDOMContentLoaded=true;mDocument->BlockDOMContentLoaded();}}boolScriptLoader::MaybeRemovedDeferRequests(){if(mDeferRequests.isEmpty()&&mDocument&&mBlockingDOMContentLoaded){mBlockingDOMContentLoaded=false;mDocument->UnblockDOMContentLoaded();returntrue;}returnfalse;}#undef TRACE_FOR_TEST#undef TRACE_FOR_TEST_BOOL#undef TRACE_FOR_TEST_NONE#undef LOG}// dom namespace}// mozilla namespace